package client;
import communication.Error;
import communication.*;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JOptionPane;

/**
 * Application client (controlleur de base)
 * 
 * @author Julien Roch, Jonathan Gander, Constantin Laurent, David Fassler
 * @version 0.1
 */
public class ClientApplication implements Observer {
	
	/**
	 * Partie Modele du MVC
	 */
	ClientInternals internals;
	/**
	 * Vue du client (conteneur de JPanel)
	 */
	private ClientView vue;
	/**
	 * Partie reseau pour les communications
	 */
	NetClient netClient;

	
	/**
	 * Lancer l'application
	 * @param args Parametres de la ligne de commande
	 */
	public static void main(String[] args) {
		new ClientApplication();
	}

	/**
	 * Constructeur. Demande les informations de connexion et lancer la
	 * connexion
	 */
	public ClientApplication() {
		init();
	}
	/**
	 * (Re-)Initialise la partie client
	 */
	public void init() {
		// Cache la vue active
		if(vue != null)
			vue.getInternalView().setVisible(false);
		
		// Ecoute du socket reseau.. Adapteur pour gerer les erreurs de connexion
		netClient = new NetClient(new LostConnectionAdapter(){
			// Si on perd la connection
			public void lostConnection(IOException e){
				// Affiche la vue de connection
				ClientApplication.this.vue.showConnectDialog();
				// Affiche l'erreur
				JOptionPane.showMessageDialog(null,
						"Erreur de connection: " + e.getMessage(), "Erreur de connection",
						JOptionPane.ERROR_MESSAGE);
				
				// Reinitalise le model et la vue a zero
				ClientApplication.this.init();
			}
			
		});
		
		// ClientApplication est observer pour etre notifiee des updates
		netClient.addObserver(this);

		// Initialise le modele
		internals = new ClientInternals(netClient);
		// Initialise la vue
		vue = new ClientView(this);
		internals.addObserver(vue);

		// Debut de l'application
		vue.showConnectDialog();
	}

	/**
	 * Permet d'obtenir la vue du client
	 * Utilise principalement depuis une vue pour changer de vue
	 * @return la vue
	 */
	public ClientView getClientView() {
		return vue;
	}
	/**
	 * Pour se connecter au serveur (avec conversion string en adresse)
	 * @param host Le serveur (nom ou ip)
	 * @param port Le port
	 */
	public void connect(String host, String port) {
		// Temporaire pour les conversions
		int port1;
		InetAddress ip = null;

		// Port string to int
		try {
			port1 = Integer.parseInt(port);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(vue.getCurrentPanel(),
					"Le port n'est pas valide", "Port invalide",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		// Host string to inetAddress
		try {
			ip = InetAddress.getByName(host);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(vue.getCurrentPanel(),
					"L'hote n'est pas valide", "Hote invalide",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		// Connecting..
		if (netClient.connect(ip, port1)) {
			vue.showLoginDialog();
		}
	}
	/**
	 * Deconnecte le client et affiche la fenetre de connection
	 */
	public void disconnect() {
		netClient.disconnect();
		vue.showConnectDialog();
	}

	/**
	 * S'authentifie aupres du serveur
	 * 
	 * @param pseudo Le pseudo
	 * @param password Le mot de passe
	 */
	public void connectionUserPassword(String pseudo, String password) {
		// Connexion du client
		netClient.sendData(new ClientConnect(pseudo, password));
		internals.userName = pseudo;
		vue.showListGameDialog();
	}
	/**
	 * Cree un compte et passe a la liste des parties
	 * @param pseudo Le pseudo
	 * @param password Le mot de passe
	 */
	public void createAccount(String pseudo, String password) {
		netClient.sendData(new ClientCreate(pseudo, password));
		internals.userName = pseudo;

		vue.showListGameDialog();
	}

	/**
	 * Action lorsque le controlleur recoit un objet
	 * 
	 * @param o   Observable l'objet responsable de l'update
	 * @param arg Object l'objet transmis par l'observable
	 */
	@Override
	public void update(Observable o, Object arg) {
		// Erreur de connexion renvoyee par le serveur
		if (Error.class.isInstance(arg)) {

			// Erreur de login (login - pass incorrect(s))
			if (((Error) arg).getError() == Error.ErrorType.LOGIN_ERROR) {
				internals.userName = null;

				vue.showLoginDialog();
				JOptionPane.showMessageDialog(vue.getCurrentPanel(),
						"Login ou mot de passe incorrect !",
						"Erreur de connection", JOptionPane.ERROR_MESSAGE);
			}
			// Erreur de login (pseudo deja connecte)
			else if (((Error) arg).getError() == Error.ErrorType.USER_ALREADY_CONNECTED) {
				internals.userName = null;

				vue.showLoginDialog();
				JOptionPane
						.showMessageDialog(
								vue.getCurrentPanel(),
								"Un pseudo identique est deja connecte sur le serveur !",
								"Erreur de connection",
								JOptionPane.ERROR_MESSAGE);
			}
			// Erreur de creation de compte (pseudo existe deja)
			else if (((Error) arg).getError() == Error.ErrorType.USER_ALREADY_EXISTS) {
				internals.userName = null;

				vue.showNewAccountDialog();
				JOptionPane.showMessageDialog(vue.getCurrentPanel(),
						"Le pseudo est deja existant !",
						"Erreur de creation de compte",
						JOptionPane.ERROR_MESSAGE);
			}
		}
		// Aucune erreur, on peut traiter ce qu'on a recu
		else if (ComSrvCli.class.isInstance(arg)) { 
			// L'objet est une communication serveur ->
			// client Utilisation de la methode dediee a ce type de transactions
			// On lui fait faire ce qu'il a a faire
			((ComSrvCli) arg).handleClientInternals(this.internals); 
		}
	}
	/**
	 * Change la position de la fenetre client
	 * @param x La position X
	 * @param y La position Y
	 */
	public void setPosition(int x, int y){
		vue.setPosition(x, y);
	}

}
